/*
 * synergy -- mouse and keyboard sharing utility
 * Copyright (C) 2016 Symless
 * 
 * This package is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * found in the file LICENSE that should have accompanied this file.
 * 
 * This package is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#define TEST_ENV

#include "synergy/KeyMap.h"

#include "test/global/gtest.h"
#include "test/global/gmock.h"

using ::testing::_;
using ::testing::NiceMock;
using ::testing::Invoke;
using ::testing::Return;
using ::testing::ReturnRef;
using ::testing::SaveArg;

namespace synergy {
    
TEST(KeyMapTests, findBestKey_requiredDown_matchExactFirstItem)
{
    KeyMap keyMap;
    KeyMap::KeyEntryList entryList;
    KeyMap::KeyItemList itemList;
    KeyMap::KeyItem item;
    item.m_required = KeyModifierShift;
    item.m_sensitive = KeyModifierShift;
    KeyModifierMask currentState = KeyModifierShift;
    KeyModifierMask desiredState = KeyModifierShift;
    itemList.push_back(item);
    entryList.push_back(itemList);

    EXPECT_EQ(0, keyMap.findBestKey(entryList, currentState, desiredState));
}
    
TEST(KeyMapTests, findBestKey_requiredAndExtraSensitiveDown_matchExactFirstItem)
{
    KeyMap keyMap;
    KeyMap::KeyEntryList entryList;
    KeyMap::KeyItemList itemList;
    KeyMap::KeyItem item;
    item.m_required = KeyModifierShift;
    item.m_sensitive = KeyModifierShift | KeyModifierAlt;
    KeyModifierMask currentState = KeyModifierShift;
    KeyModifierMask desiredState = KeyModifierShift;
    itemList.push_back(item);
    entryList.push_back(itemList);

    EXPECT_EQ(0, keyMap.findBestKey(entryList, currentState, desiredState));
}

TEST(KeyMapTests, findBestKey_requiredAndExtraSensitiveDown_matchExactSecondItem)
{
    KeyMap keyMap;
    KeyMap::KeyEntryList entryList;
    KeyMap::KeyItemList itemList1;
    KeyMap::KeyItem item1;
    item1.m_required = KeyModifierAlt;
    item1.m_sensitive = KeyModifierShift | KeyModifierAlt;
    KeyMap::KeyItemList itemList2;
    KeyMap::KeyItem item2;
    item2.m_required = KeyModifierShift;
    item2.m_sensitive = KeyModifierShift | KeyModifierAlt;
    KeyModifierMask currentState = KeyModifierShift;
    KeyModifierMask desiredState = KeyModifierShift;
    itemList1.push_back(item1);
    itemList2.push_back(item2);
    entryList.push_back(itemList1);
    entryList.push_back(itemList2);

    EXPECT_EQ(1, keyMap.findBestKey(entryList, currentState, desiredState));
}
    
TEST(KeyMapTests, findBestKey_extraSensitiveDown_matchExactSecondItem)
{
    KeyMap keyMap;
    KeyMap::KeyEntryList entryList;
    KeyMap::KeyItemList itemList1;
    KeyMap::KeyItem item1;
    item1.m_required = 0;
    item1.m_sensitive = KeyModifierAlt;
    KeyMap::KeyItemList itemList2;
    KeyMap::KeyItem item2;
    item2.m_required = 0;
    item2.m_sensitive = KeyModifierShift;
    KeyModifierMask currentState = KeyModifierAlt;
    KeyModifierMask desiredState = KeyModifierAlt;
    itemList1.push_back(item1);
    itemList2.push_back(item2);
    entryList.push_back(itemList1);
    entryList.push_back(itemList2);
        
    EXPECT_EQ(1, keyMap.findBestKey(entryList, currentState, desiredState));
}

TEST(KeyMapTests, findBestKey_noRequiredDown_matchOneRequiredChangeItem)
{
    KeyMap keyMap;
    KeyMap::KeyEntryList entryList;
    KeyMap::KeyItemList itemList1;
    KeyMap::KeyItem item1;
    item1.m_required = KeyModifierShift | KeyModifierAlt;
    item1.m_sensitive = KeyModifierShift | KeyModifierAlt;
    KeyMap::KeyItemList itemList2;
    KeyMap::KeyItem item2;
    item2.m_required = KeyModifierShift;
    item2.m_sensitive = KeyModifierShift | KeyModifierAlt;
    KeyModifierMask currentState = 0;
    KeyModifierMask desiredState = 0;
    itemList1.push_back(item1);
    itemList2.push_back(item2);
    entryList.push_back(itemList1);
    entryList.push_back(itemList2);
    
    EXPECT_EQ(1, keyMap.findBestKey(entryList, currentState, desiredState));
}

TEST(KeyMapTests, findBestKey_onlyOneRequiredDown_matchTwoRequiredChangesItem)
{
    KeyMap keyMap;
    KeyMap::KeyEntryList entryList;
    KeyMap::KeyItemList itemList1;
    KeyMap::KeyItem item1;
    item1.m_required = KeyModifierShift | KeyModifierAlt | KeyModifierControl;
    item1.m_sensitive = KeyModifierShift | KeyModifierAlt | KeyModifierControl;
    KeyMap::KeyItemList itemList2;
    KeyMap::KeyItem item2;
    item2.m_required = KeyModifierShift| KeyModifierAlt;
    item2.m_sensitive = KeyModifierShift | KeyModifierAlt | KeyModifierControl;
    KeyModifierMask currentState = 0;
    KeyModifierMask desiredState = 0;
    itemList1.push_back(item1);
    itemList2.push_back(item2);
    entryList.push_back(itemList1);
    entryList.push_back(itemList2);

    EXPECT_EQ(1, keyMap.findBestKey(entryList, currentState, desiredState));
}
    
TEST(KeyMapTests, findBestKey_noRequiredDown_cannotMatch)
{
    KeyMap keyMap;
    KeyMap::KeyEntryList entryList;
    KeyMap::KeyItemList itemList;
    KeyMap::KeyItem item;
    item.m_required = 0xffffffff;
    item.m_sensitive = 0xffffffff;
    KeyModifierMask currentState = 0;
    KeyModifierMask desiredState = 0;
    itemList.push_back(item);
    entryList.push_back(itemList);
    
    EXPECT_EQ(-1, keyMap.findBestKey(entryList, currentState, desiredState));
}
    
TEST(KeyMapTests, isCommand_shiftMask_returnFalse)
{
    KeyMap keyMap;
    KeyModifierMask mask= KeyModifierShift;
    
    EXPECT_FALSE(keyMap.isCommand(mask));
}
    
TEST(KeyMapTests, isCommand_controlMask_returnTrue)
{
    KeyMap keyMap;
    KeyModifierMask mask= KeyModifierControl;
        
    EXPECT_EQ(true, keyMap.isCommand(mask));
}
    
TEST(KeyMapTests, isCommand_alternateMask_returnTrue)
{
    KeyMap keyMap;
    KeyModifierMask mask= KeyModifierAlt;
    
    EXPECT_EQ(true, keyMap.isCommand(mask));
}

TEST(KeyMapTests, isCommand_alternateGraphicMask_returnTrue)
{
    KeyMap keyMap;
    KeyModifierMask mask= KeyModifierAltGr;
    
    EXPECT_EQ(true, keyMap.isCommand(mask));
}
    
TEST(KeyMapTests, isCommand_metaMask_returnTrue)
{
    KeyMap keyMap;
    KeyModifierMask mask= KeyModifierMeta;
    
    EXPECT_EQ(true, keyMap.isCommand(mask));
}

TEST(KeyMapTests, isCommand_superMask_returnTrue)
{
    KeyMap keyMap;
    KeyModifierMask mask= KeyModifierSuper;
    
    EXPECT_EQ(true, keyMap.isCommand(mask));
}

TEST(KeyMapTests, mapkey_handles_setmodifier_with_no_mapped)
{
    KeyMap keyMap {};
    KeyMap::Keystroke stroke('A', true, false, 1);
    KeyMap::KeyItem keyItem;
    keyItem.m_button = 'A';
    keyItem.m_group = 1;
    keyItem.m_id = 'A';
    keyMap.addKeyEntry(keyItem);
    keyMap.finish();
    KeyMap::Keystrokes strokes {stroke};
    KeyMap::ModifierToKeys activeModifiers {};
    KeyModifierMask currentState {};
    KeyModifierMask desiredMask {};
    auto result = keyMap.mapKey(strokes, kKeySetModifiers, 1, activeModifiers, currentState, desiredMask, false);    
    EXPECT_FALSE(result == nullptr);
    desiredMask = KeyModifierControl;
    result = keyMap.mapKey(strokes, kKeySetModifiers, 1, activeModifiers, currentState, desiredMask, false);    
    EXPECT_TRUE(result == nullptr);
}
    

}
